#include "lib_syscall.h"
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <sys/file.h>
#include "stdlib.h"
#include "file.h"
#include "os_cfg.h"
#include "main.h"
#include <sys/file.h>
#include "tty.h"

static int str_count(char **argv)
{
    int count = 0;
    if (argv)
    {
        while (*argv++ != 0)
        {
            count++;
        }
    }
    return count;
}

void printf_test()
{
    sbrk(0);
    sbrk(100);
    sbrk(200);
    sbrk(500);
    sbrk(4096 * 2 + 200);
    sbrk(4096 * 5 + 1234);

    printf("hello world!\n");
    printf("abef\b\b\b\bcd\n");                     // cdef
    printf("abcd\x7f;fg\n");                        // abc;fg
    printf("\0337Hello world,world!\0338123\n");    // 123lo world!
    printf("12\t123456\t78\t123\t123456789\t0123\n");
    printf("123456789\t123\t45\n");
    printf("\033[31;42mHello world!\033[39;49m123\n");
    printf("\0337Hello,word!\0338123\n");                // ESC 7,8 输出123lo,word!
    printf("\033[31;42mHello,word!\033[39;49m123\n");    // ESC [pn m, Hello,world红色，>其余绿色
    printf("123\033[2DHello,word!\n");                   // 光标左移2，1Hello,word!
    printf("123\033[2CHello,word!\n");                   // 光标右移2，123  Hello,word!

    printf("\033[31m");               // ESC [pn m, Hello,world红色，其余绿色
    printf("\033[10;10H test!\n");    // 定位到10, 10，test!
    printf("\033[20;20H test!\n");    // 定位到20, 20，test!
    printf("\033[32;25;39m123\n");    // ESC [pn m, Hello,world红色，其余绿色
                                      // printf("\033[2J\n");
    printf("fuck the console");
    // printf("\033[1J\n");
}

static cli_t       cli;
char               cmd_buf[256];
static const char *promot = "sh >>";

static int do_help(int argc, char **argv)
{
    const cli_cmd_t *start = cli.cmd_start;
    while (start < cli.cmd_end)
    {
        printf("%s %s\n", start->name, start->usage);
        start++;
    }
    return 0;
}
static int do_clear(int argc, char **argv)
{
    printf("%s", ESC_CLEAR_SCREEN);
    printf("%s", ESC_MOV_CURSOR(0, 0));
    return 0;
}
static int do_echo(int argc, char **argv)
{
    if (argc == 1)
    {
        char msg_buf[128];
        fgets(msg_buf, sizeof(msg_buf), stdin);
        msg_buf[sizeof(msg_buf) - 1] = '\0';
        puts(msg_buf);
        return 0;
    }
    int count = 1;
    int ch;
    while ((ch = getopt(argc, argv, "n:h")) != -1)
    {
        switch (ch)
        {
        case 'n':
            count = atoi(optarg);
            break;
        case 'h':
            puts("echo any message");
            puts("Usage: echo [-n count] msg");
            optind = 1;
            return 0;
        case '?':
            if (optarg)
            {
                fprintf(stderr, "Unknown option:-%s\n", optarg);
            }
            optind = 1;
            return -1;
        }
    }
    if (optind > argc - 1)
    {
        fprintf(stderr, "message is empty\n");
        optind = 1;
        return -1;
    }
    char *msg = argv[optind];
    for (int i = 0; i < count; i++)
    {
        puts(msg);
    }
    optind = 1;
    return 0;
}
static int do_exit(int argc, char **argv)
{
    exit(0);
    return 0;
}
static int do_ls(int argc, char **argv)
{
    DIR *p_dir = opendir(argv[0]);
    if (p_dir == (DIR *)0)
    {
        printf("open dir failed\n");
        return -1;
    }
    struct dirent *entry;
    while ((entry = readdir(p_dir)) != (struct dirent *)0)
    {
        printf("%c\t%s\t%d\n", entry->type == FILE_DIR ? 'd' : 'f', entry->name, entry->size);
    }
    closedir(p_dir);
    return 0;
}
static int do_less(int argc, char **argv)
{
    int line_mode = 0;
    int ch;
    while ((ch = getopt(argc, argv, "lh")) != -1)
    {
        switch (ch)
        {
        case 'l':
            line_mode = 1;
            break;
        case 'h':
            puts("show file content");
            puts("Usage: less [-l] file");
            optind = 1;
            return 0;
        case '?':
            if (optarg)
            {
                fprintf(stderr, "Unknown option:-%s\n", optarg);
            }
            optind = 1;
            return -1;
        default:
            break;
        }
    }
    if (optind > argc - 1)
    {
        fprintf(stderr, "no file\n");
        optind = 1;
        return -1;
    }
    FILE *file = fopen(argv[optind], "r");
    if (file == NULL)
    {
        fprintf(stderr, "open file failed. %s\n", argv[optind]);
        optind = 1;
        return -1;
    }
    char *buf = (char *)malloc(255);
    if (line_mode == 0)
    {
        while (fgets(buf, 255, file) != NULL)
        {
            fputs(buf, stdout);
        }
    }
    else
    {
        setvbuf(stdin, NULL, _IONBF, 0);
        ioctl(0, TTY_CMD_ECHO, 0, 0);
        while (1)
        {
            char *b = fgets(buf, 255, file);
            if (b == NULL)
            {
                break;
            }
            fputs(buf, stdout);
            char ch;
            while ((ch = fgetc(stdin)) != 'n')
            {
                if (ch == 'q')
                {
                    puts("\n");
                    goto less_quit;
                }
            }
        }
    less_quit:
        setvbuf(stdin, NULL, _IOLBF, BUFSIZ);
        ioctl(0, TTY_CMD_ECHO, 1, 0);
    }
    free(buf);
    fclose(file);
    optind = 1;
    return 0;
}
static int do_cp(int argc, char **argv)
{
    if (argc < 3)
    {
        fprintf(stderr, "fault usage,dest,src needed\n");
        return -1;
    }
    FILE *from, *to;
    from = fopen(argv[1], "rb");
    to   = fopen(argv[2], "wb");
    if (!from || !to)
    {
        fprintf(stderr, "open file failed\n");
        goto cp_failed;
    }
    char *buf = (char *)malloc(255);
    int   size;
    while ((size = fread(buf, 1, 255, from)) > 0)
    {
        fwrite(buf, 1, size, to);
    }
    free(buf);
    if (from)
    {
        fclose(from);
    }
    if (to)
    {
        fclose(to);
    }
cp_failed:
    return -1;
}
static int do_rm(int argc, char **argv)
{
    if (argc < 2)
    {
        fprintf(stderr, "no file\n");
        return -1;
    }
    int err = unlink(argv[1]);
    if (err < 0)
    {
        fprintf(stderr, "rm file failed:%s\n", argv[1]);
        return err;
    }
    return 0;
}
static const cli_cmd_t cmd_list[] = {
    {
        .name    = "help",
        .usage   = "help -- list supported command",
        .do_func = do_help,
    },
    {
        .name    = "clear",
        .usage   = "clear -- clear the screen",
        .do_func = do_clear,
    },
    {
        .name    = "echo",
        .usage   = "echo [-n count] msg---echo something",
        .do_func = do_echo,
    },
    {
        .name    = "less",
        .usage   = "less [-l] file",
        .do_func = do_less,
    },
    {
        .name    = "ls",
        .usage   = "ls -- list directory",
        .do_func = do_ls,
    },
    {
        .name    = "cp",
        .usage   = "cp src dest",
        .do_func = do_cp,
    },
    {
        .name    = "rm",
        .usage   = "rm file - remove file",
        .do_func = do_rm,

    },
    {
        .name    = "quit",
        .usage   = "quit from shell",
        .do_func = do_exit,
    },
};
static void cli_init(const char *promot, const cli_cmd_t *cmd_list, int size)
{
    cli.promot = promot;
    memset(cli.curr_input, 0, CLI_INPUT_SIZE);
    cli.cmd_start = cmd_list;
    cli.cmd_end   = cmd_list + size;
}
static void show_promot(void)
{
    printf("%s", cli.promot);
    fflush(stdout);
}

const static cli_cmd_t *find_builtin(const char *name)
{
    for (const cli_cmd_t *cmd = cli.cmd_start; cmd < cli.cmd_end; cmd++)
    {
        if (strcmp(cmd->name, name) != 0)
        {
            continue;
        }
        return cmd;
    }
    return (cli_cmd_t *)0;
}
static void run_builtin(const cli_cmd_t *cmd, int arg_count, char **arg_value)
{
    int ret = cmd->do_func(arg_count, arg_value);
    if (ret < 0)
    {
        fprintf(stderr, "error: %d\n", ret);
    }
}
static void run_exec_file(const char *path, int argc, char **argv)
{
    int pid = fork();
    if (pid < 0)
    {
        fprintf(stderr, "fork failed:%s\n", path);
    }
    else if (pid == 0)
    {
#if 0
        for(int i=0;i<argc;i++){
            msleep(1000);
            printf("arg %d = %s\n",i,argv[i]);
        }
#endif
        char *env[] = {"env0", "env1", (char *)0};
        int   err   = execve(path, argv, env);
        if (err < 0)
        {
            fprintf(stderr, "exec failed:%s", path);
        }
        _exit(-1);
    }
    else
    {
        int status;
        int pid = wait(&status);
        fprintf(stderr, "cmd %s,child pid:%x,status: %d\n", path, pid, status);
    }
}
static const char *find_exec_path(const char *filename)
{
    static char path[255];
    int         fd = open(filename, O_RDONLY);
    if (fd < 0)
    {
        sprintf(path, "%s.elf", filename);
        fd = open(path, 0);
        if (fd < 0)
        {
            return (const char *)0;
        }
        close(fd);
    }
    else
    {
        close(fd);
        return filename;
    }
    return path;
}
int main(int argc, char **argv)
{
    char buf[1000];
    memset(buf, 0, 1000);
    // fs
    // int fd=open("tty:0",0); //fd=0
    open(argv[0], O_RDWR);    // fd=0
    dup(0);
    dup(0);
    // fprintf(stderr,"error\n");
    // char temp[]="fuck the world\n";
    // write(fd,temp,sizeof(temp));
    // printf_test();

    int count = str_count(argv);
    printf("welcome to shell\n");
    for (int i = 0; i < count; i++)
    {
        printf("from shell main: arg:%s\n", argv[i]);
    }
    puts("hello from x86 os\n");
    printf("os version :%s\n", OS_VERSION);
    puts("author: ydx\n");
    puts("create data: 2025-06-25\n");

    cli_init(promot, cmd_list, sizeof(cmd_list) / sizeof(cmd_list[0]));

    // fork();
    // yield();
    for (;;)
    {
        show_promot();
        // gets(cli.curr_input);
        char *str = fgets(cli.curr_input, CLI_INPUT_SIZE, stdin);
        if (!str)
        {
            continue;
        }
        char *cr = strchr(cli.curr_input, '\r');
        if (cr)
        {
            *cr = '\0';
        }
        cr = strchr(cli.curr_input, '\n');
        if (cr)
        {
            *cr = '\0';
        }
        const char *space = " ";
        char       *token = strtok(cli.curr_input, space);
        int         _argc = 0;
        char       *_argv[CLI_MAX_ARG_COUNT];
        memset(_argv, 0, sizeof(_argv));
        while (token)
        {
            _argv[_argc++] = token;
            token          = strtok(NULL, space);
        }
        if (_argc == 0)
        {
            continue;
        }
        const cli_cmd_t *cmd = find_builtin(_argv[0]);
        if (cmd)
        {
            run_builtin(cmd, _argc, _argv);
            continue;
        }
        // run_exec
        const char *path = find_exec_path(_argv[0]);
        if (path)
        {
            run_exec_file(path, _argc, _argv);
            continue;
        }
        // exec
        fprintf(stderr, ESC_COLOR_ERROR "Unknown command:%s\n" ESC_COLOR_DEFAULT, cli.curr_input);
    }
}